// ==========================================================================
// gin-sns自动生成业务逻辑层相关代码，只生成一次，按需修改,再次生成不会覆盖.
// date：2021-02-21 13:45:54 +0800 CST
// path: app/service/module/user_service.go
// author：jon
// ==========================================================================
package module

import (
	"errors"
	"gin-sns/app/common"
	"gin-sns/app/common/oss"
	"gin-sns/app/common/uploader"
	"gin-sns/app/common/validate"
	"gin-sns/app/dao/module/user"
	"gin-sns/app/model"
	thirdaccountModel "gin-sns/app/model/module/thirdaccount"
	userModel "gin-sns/app/model/module/user"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"

	"gin-sns/app/ginframe/tools"
	"gin-sns/app/ginframe/utils/convert"
	"gin-sns/app/ginframe/utils/page"
)

var UserService = newUserService()

func newUserService() *userService {
	return &userService{}
}

type userService struct {
}

//根据主键查询数据
func (s *userService) SelectRecordById(id int64) (*userModel.Entity, error) {
	return user.UserDao.SelectById(id)
}

//
func (s *userService) SelectRecordsIn(idarr []int64) ([]userModel.Entity, error) {

	list, err := user.UserDao.FindIn("id", idarr)
	if list != nil && len(list) > 0 {
		err = errors.New("not found")
		list = nil
	}
	return list, err
}

//根据主键删除数据
func (s *userService) DeleteRecordById(id int64) bool {
	return user.UserDao.DeleteById(id)
}

//批量删除数据记录
func (s *userService) DeleteRecordByIds(ids string) int64 {
	ida := convert.ToInt64Array(ids, ",")
	result, err := user.UserDao.DeleteBatch(ida...)
	if err != nil {
		return 0
	}
	return result
}

//添加数据
func (s *userService) AddSave(req *userModel.AddReq, ctx *gin.Context) (int64, error) {
	var entity userModel.Entity

	//entity.Id = req.Id
	entity.UserName = req.UserName
	entity.UserMobile = req.UserMobile
	entity.UserEmail = req.UserEmail
	entity.Password = req.Password
	entity.Type = req.Type
	entity.Nickname = req.Nickname
	entity.Sex = req.Sex
	entity.Avatar = req.Avatar
	entity.Work = req.Work
	entity.Signature = req.Signature
	entity.Description = req.Description
	entity.Province = req.Province
	entity.City = req.City
	entity.Area = req.Area
	entity.ContactAddr = req.ContactAddr
	entity.CountFollower = req.CountFollower
	entity.CountFollowee = req.CountFollowee
	entity.CountQuestion = req.CountQuestion
	entity.CountAnswer = req.CountAnswer
	entity.CountPost = req.CountPost
	entity.CountBlog = req.CountBlog
	entity.Level = req.Level
	entity.Integral = req.Integral
	entity.Status = req.Status
	entity.CreateTime = time.Now()
	//entity.CreateBy = ""

	//user := system.UserService.GetProfile(ctx)

	//if user != nil {
	//	entity.CreateBy = user.LoginName
	//}

	_, err := user.UserDao.Insert(&entity)
	return entity.Id, err
}

func (s *userService) Insert(entity *userModel.Entity) error {
	_, err := user.UserDao.Insert(entity)
	return err
}

//修改数据
func (s *userService) EditSave(req *userModel.EditReq, ctx *gin.Context) (int64, error) {
	entity := &userModel.Entity{Id: req.Id}
	ok, err := user.UserDao.FindOne(entity)

	if err != nil {
		return 0, err
	}

	if !ok {
		return 0, errors.New("数据不存在")
	}

	entity.UserName = req.UserName
	entity.UserMobile = req.UserMobile
	entity.UserEmail = req.UserEmail
	entity.Password = req.Password
	entity.Type = req.Type
	entity.Nickname = req.Nickname
	entity.Sex = req.Sex
	entity.Avatar = req.Avatar
	entity.Work = req.Work
	entity.Signature = req.Signature
	entity.Description = req.Description
	entity.Province = req.Province
	entity.City = req.City
	entity.Area = req.Area
	entity.ContactAddr = req.ContactAddr
	entity.CountFollower = req.CountFollower
	entity.CountFollowee = req.CountFollowee
	entity.CountQuestion = req.CountQuestion
	entity.CountAnswer = req.CountAnswer
	entity.CountPost = req.CountPost
	entity.CountBlog = req.CountBlog
	entity.Level = req.Level
	entity.Integral = req.Integral
	entity.Status = req.Status
	//entity.UpdateTime = time.Now()
	//entity.UpdateBy = ""

	//user := system.UserService.GetProfile(ctx)

	//if user == nil {
	//	entity.UpdateBy = user.LoginName
	//}

	return user.UserDao.Update(entity)
}

func (s *userService) Update(entity *userModel.Entity) error {
	_, err := user.UserDao.Update(entity)
	return err
}

//根据条件查询数据
func (s *userService) SelectListAll(params *userModel.SelectPageReq) ([]userModel.Entity, error) {
	return user.UserDao.SelectListAll(params)
}

//根据条件分页查询数据
func (s *userService) SelectListByPage(params *userModel.SelectPageReq) ([]userModel.Entity, *page.Paging, error) {
	return user.UserDao.SelectListByPage(params)
}

// 导出excel
func (s *userService) Export(param *userModel.SelectPageReq) (string, error) {
	head := []string{"", "用户名", "手机", "Email", "密码", "类型", "昵称", "性别1男2女3保密", "头像", "工作职位", "个性签名", "自我介绍", "所在省", "所在城市", "所在地区", "联系地址", "被关注数量", "关注数量", "被关注数量", "被关注数量", "被关注数量", "被关注数量", "级别", "积分", "0未审核 1正常状态 2审核未通过 3删除", "创建时间", "更新时间"}
	col := []string{"id", "user_name", "user_mobile", "user_email", "password", "type", "nickname", "sex", "avatar", "work", "signature", "description", "province", "city", "area", "contact_addr", "count_follower", "count_followee", "count_question", "count_answer", "count_post", "count_blog", "level", "integral", "status", "create_time", "update_time"}
	return user.UserDao.SelectListExport(param, head, col)
}

func (s *userService) GetByEmail(email string) *userModel.Entity {
	entity, _ := user.UserDao.SelectUserByEmail(email)

	return entity
}

func (s *userService) GetByUsername(username string) *userModel.Entity {
	entity, _ := user.UserDao.SelectUserByUserName(username)

	return entity
}

// 注册
func (s *userService) SignUp(username, email, nickname, avatar, password, rePassword string) (*userModel.Entity, error) {
	username = strings.TrimSpace(username)
	email = strings.TrimSpace(email)
	nickname = strings.TrimSpace(nickname)

	if len(nickname) == 0 {
		return nil, errors.New("昵称不能为空")
	}

	if err := common.IsValidateUsername(username); err != nil {
		return nil, err
	}

	// 验证密码
	err := common.IsValidatePassword(password, rePassword)
	if err != nil {
		return nil, err
	}

	// 如果设置了邮箱，那么需要验证邮箱
	if len(email) > 0 {
		if err := common.IsValidateEmail(email); err != nil {
			return nil, err
		}
		if s.GetByEmail(email) != nil {
			return nil, errors.New("邮箱：" + email + " 已被占用")
		}
	}

	// 验证用户名是否存在
	if s.isUsernameExists(username) {
		return nil, errors.New("用户名：" + username + " 已被占用")
	}

	addReq := &userModel.AddReq{
		UserName:  username,
		UserEmail: email,
		Nickname:  nickname,
		Password:  tools.EncodePassword(password),
		Avatar:    avatar,
		Status:    model.UserStatusOk,
	}

	_, err = s.AddSave(addReq, nil)
	if err != nil {
		return nil, err
	}
	//cache.UserCache.Invalidate(user.Id)
	return user.UserDao.SelectUserByUserName(username)
}

// 登录
func (s *userService) SignIn(username, password string) (*userModel.Entity, error) {
	if len(username) == 0 {
		return nil, errors.New("用户名/邮箱不能为空")
	}
	if len(password) == 0 {
		return nil, errors.New("密码不能为空")
	}
	var user *userModel.Entity = nil
	if err := common.IsValidateEmail(username); err == nil { // 如果用户输入的是邮箱
		user = s.GetByEmail(username)
	} else {
		user = s.GetByUsername(username)
	}
	if user == nil {
		return nil, errors.New("用户不存在")
	}
	if !tools.ValidatePassword(user.Password, password) {
		return nil, errors.New("密码错误")
	}
	return user, nil
}

// 第三方账号登录
func (s *userService) SignInByThirdAccount(thirdAccount *thirdaccountModel.Entity) (*userModel.Entity, error) {
	user, _ := s.SelectRecordById(thirdAccount.UserId)
	if user != nil {
		return user, nil
	}

	avatar, copyAvatarErr := oss.CopyImage(thirdAccount.Avatar)
	if copyAvatarErr != nil {
		logrus.Error("复制第三方头像异常", thirdAccount.Avatar, copyAvatarErr)
		avatar = thirdAccount.Avatar
	}
	AddReq := &userModel.AddReq{
		UserName: thirdAccount.Nickname, //todo
		Nickname: thirdAccount.Nickname,
		Avatar:   avatar,
		Status:   model.UserStatusOk,
	}

	if _, err := s.AddSave(AddReq, nil); err != nil {
		return nil, err
	}

	//todo
	//user, err := SelectRecordById(thirdAccount.UserId)
	//if user.Id > 0 {
	//	err = thirdAccountModel.UpdateColumn(tx, thirdAccount.Id, "user_id", user.Id)
	//	if err != nil {
	//		return err
	//	}
	//}
	//
	//if err != nil {
	//	return nil, tools.FromError(err)
	//}

	//cache.UserCache.Invalidate(user.Id)
	return user, nil
}

// HandleThirdAvatar 处理第三方头像
func (s *userService) HandleThirdAvatar(thirdAvatar string) string {
	if tools.IsBlank(thirdAvatar) {
		return ""
	}
	avatar, err := uploader.CopyImage(thirdAvatar)
	if err != nil {
		return ""
	}
	return avatar
}

// isEmailExists 邮箱是否存在
func (s *userService) isEmailExists(email string) bool {
	if len(email) == 0 { // 如果邮箱为空，那么就认为是不存在
		return false
	}
	return s.GetByEmail(email) != nil
}

// isUsernameExists 用户名是否存在
func (s *userService) isUsernameExists(username string) bool {
	return s.GetByUsername(username) != nil
}

// UpdateAvatar 更新头像
func (s *userService) UpdateAvatar(userId int64, avatar string) error {
	entity, err := s.SelectRecordById(userId)
	if err != nil || entity == nil {
		return errors.New("not found")
	}
	entity.Avatar = avatar
	_, err = user.UserDao.Update(entity)

	return err
}

// UpdateBackgroundImage 修改背景图
/*func (s *userService)UpdateBackgroundImage(userId int64, backgroundImage string) error {

	entity, err := SelectRecordById(userId)
	if err != nil || entity == nil {
		return errors.New("not found")
	}
	entity.backgroundImage = backgroundImage
	_, err = entity.Update()

	return err
}

// SetUsername 设置用户名
func(s *userService) SetUsername(userId int64, username string) error {
	username = strings.TrimSpace(username)
	if err := validate.IsUsername(username); err != nil {
		return err
	}

	user := s.Get(userId)
	if len(user.Username.String) > 0 {
		return errors.New("你已设置了用户名，无法重复设置。")
	}
	if s.isUsernameExists(username) {
		return errors.New("用户名：" + username + " 已被占用")
	}
	return s.UpdateColumn(userId, "username", username)
}

// SetEmail 设置密码
func(s *userService) SetEmail(userId int64, email string) error {
	email = strings.TrimSpace(email)
	if err := validate.IsEmail(email); err != nil {
		return err
	}
	user := s.Get(userId)
	if user == nil {
		return errors.New("用户不存在")
	}
	if user.Email.String == email {
		// 用户邮箱没做变更
		return nil
	}
	if s.isEmailExists(email) {
		return errors.New("邮箱：" + email + " 已被占用")
	}
	return s.Updates(userId, map[string]interface{}{
		"email":          email,
		"email_verified": false,
	})
}

// SetPassword 设置密码
func(s *userService) SetPassword(userId int64, password, rePassword string) error {
	if err := validate.IsPassword(password, rePassword); err != nil {
		return err
	}
	user := SelectRecordById(userId)
	if len(user.Password) > 0 {
		return errors.New("你已设置了密码，如需修改请前往修改页面。")
	}
	password = tools.EncodePassword(password)
	return s.UpdateColumn(userId, "password", password)
}

// UpdatePassword 修改密码
func (s *userService)UpdatePassword(userId int64, oldPassword, password, rePassword string) error {
	if err := validate.IsPassword(password, rePassword); err != nil {
		return err
	}
	user := s.Get(userId)

	if len(user.Password) == 0 {
		return errors.New("你没设置密码，请先设置密码")
	}

	if !tools.ValidatePassword(user.Password, oldPassword) {
		return errors.New("旧密码验证失败")
	}

	return s.UpdateColumn(userId, "password", tools.EncodePassword(password))
}

// IncrTopicCount topic_count + 1
func(s *userService) IncrTopicCount(userId int64) int {
	t := userModel.Get(tools.DB(), userId)
	if t == nil {
		return 0
	}
	topicCount := t.TopicCount + 1
	if err := userModel.UpdateColumn(tools.DB(), userId, "topic_count", topicCount); err != nil {
		logrus.Error(err)
	} else {
		cache.UserCache.Invalidate(userId)
	}
	return topicCount
}

// IncrCommentCount comment_count + 1
func(s *userService) IncrCommentCount(userId int64) int {
	t := userModel.Get(tools.DB(), userId)
	if t == nil {
		return 0
	}
	commentCount := t.CommentCount + 1
	if err := userModel.UpdateColumn(tools.DB(), userId, "comment_count", commentCount); err != nil {
		logrus.Error(err)
	} else {
		cache.UserCache.Invalidate(userId)
	}
	return commentCount
}

// SyncUserCount 同步用户计数
func (s *userService)SyncUserCount() {
	s.Scan(func(users []userModel.Entity) {

		for _, user := range users {

			topicCount := topicModel.Count(tools.DB(), tools.NewSqlCnd().Eq("user_id", user.Id).Eq("status", constants.StatusOk))
			commentCount := commentModel.Count(tools.DB(), tools.NewSqlCnd().Eq("user_id", user.Id).Eq("status", constants.StatusOk))
			_ = userModel.UpdateColumn(tools.DB(), user.Id, "topic_count", topicCount)
			_ = userModel.UpdateColumn(tools.DB(), user.Id, "comment_count", commentCount)
			cache.UserCache.Invalidate(user.Id)

		}
	})
}

// SendEmailVerifyEmail 发送邮箱验证邮件
func (s *userService)SendEmailVerifyEmail(userId int64) error {
	user := s.Get(userId)
	if user == nil {
		return errors.New("用户不存在")
	}
	if user.EmailVerified {
		return errors.New("用户邮箱已验证")
	}
	if err := validate.IsEmail(user.Email.String); err != nil {
		return err
	}
	var (
		token     = tools.UUID()
		url       = urls.AbsUrl("/user/email/verify?token=" + token)
		link      = &model.ActionLink{Title: "点击这里验证邮箱>>", Url: url}
		siteTitle = config.AppConf.ssiteTitle
		subject   = "邮箱验证 - " + siteTitle
		title     = "邮箱验证 - " + siteTitle
		content   = "该邮件用于验证你在 " + siteTitle + " 中设置邮箱的正确性，请在" + strconv.Itoa(emailVerifyExpireHour) + "小时内完成验证。验证链接：" + url
	)
	return tools.DB().Transaction(func(tx *gorm.DB) error {
		if err := emailCodeModel.Create(tx, &model.EmailCode{
			Model:      model.Model{},
			UserId:     userId,
			Email:      user.Email.String,
			Code:       "",
			Token:      token,
			Title:      title,
			Content:    content,
			Used:       false,
			CreateTime: date.NowTimestamp(),
		}); err != nil {
			return nil
		}
		if err := email.SendTemplateEmail(nil, user.Email.String, subject, title, content, "", link); err != nil {
			return err
		}
		return nil
	})
}

// VerifyEmail 验证邮箱
func(s *userService) VerifyEmail(userId int64, token string) error {
	emailCode := EmailCodeService.FindOne(tools.NewSqlCnd().Eq("token", token))
	if emailCode == nil || emailCode.Used {
		return errors.New("非法请求")
	}
	if emailCode.UserId != userId {
		return errors.New("非法验证码")
	}

	user := s.Get(userId)
	if user == nil || emailCode.Email != user.Email.String {
		return errors.New("验证码过期")
	}
	if date.FromTimestamp(emailCode.CreateTime).Add(time.Hour * time.Duration(emailVerifyExpireHour)).Before(time.Now()) {
		return errors.New("验证邮件已过期")
	}
	return tools.DB().Transaction(func(tx *gorm.DB) error {
		if err := userModel.UpdateColumn(tx, emailCode.UserId, "email_verified", true); err != nil {
			return err
		}
		cache.UserCache.Invalidate(emailCode.UserId)
		return emailCodeModel.UpdateColumn(tx, emailCode.Id, "used", true)
	})
}

// CheckPostStatus 用于在发表内容时检查用户状态
func (s *userService)CheckPostStatus(user *userModel.Entity) *tools.CodeError {
	if user == nil {
		return tools.ErrorNotLogin
	}
	if user.Status != constants.StatusOk {
		return common.UserDisabled
	}
	if user.IsForbidden() {
		return common.ForbiddenError
	}
	observeSeconds := SysConfigService.GetInt(constants.SysConfigUserObserveSeconds)
	if user.InObservationPeriod(observeSeconds) {
		return tools.NewError(common.InObservationPeriod.Code, "账号尚在观察期，观察期时长："+strconv.Itoa(observeSeconds)+"秒，请稍后再试")
	}
	return nil
}

// IncrScoreForPostTopic 发帖获积分
func(s *userService) IncrScoreForPostTopic(topic *model.Topic) {
	config := SysConfigService.GetConfig()
	if config.ScoreConfig.PostTopicScore <= 0 {
		logrus.Info("请配置发帖积分")
		return
	}
	err := s.addScore(topic.UserId, config.ScoreConfig.PostTopicScore, constants.EntityTopic,
		strconv.FormatInt(topic.Id, 10), "发表话题")
	if err != nil {
		logrus.Error(err)
	}
}

// IncrScoreForPostComment 跟帖获积分
func(s *userService) IncrScoreForPostComment(comment *commentModel.Entity) {
	// 非话题跟帖，跳过
	if comment.EntityType != constants.EntityTopic {
		return
	}
	config := SysConfigService.GetConfig()
	if config.ScoreConfig.PostCommentScore <= 0 {
		logrus.Info("请配置跟帖积分")
		return
	}
	err := s.addScore(comment.UserId, config.ScoreConfig.PostCommentScore, constants.EntityComment,
		strconv.FormatInt(comment.Id, 10), "发表跟帖")
	if err != nil {
		logrus.Error(err)
	}
}

// IncrScore 增加分数
func(s *userService) IncrScore(userId int64, score int, sourceType, sourceId, description string) error {
	if score <= 0 {
		return errors.New("分数必须为正数")
	}
	return s.addScore(userId, score, sourceType, sourceId, description)
}

// DecrScore 减少分数
func (s *userService)DecrScore(userId int64, score int, sourceType, sourceId, description string) error {
	if score <= 0 {
		return errors.New("分数必须为正数")
	}
	return s.addScore(userId, -score, sourceType, sourceId, description)
}

// addScore 加分数，也可以加负数
func(s *userService) addScore(userId int64, score int, sourceType, sourceId, description string) error {
	if score == 0 {
		return errors.New("分数不能为0")
	}
	user, _ := SelectRecordById(userId)
	if user == nil {
		return errors.New("用户不存在")
	}
	score = user.Integral + score
	if err := s.Updates(userId, map[string]interface{}{
		"score":       score,
		"update_time": date.NowTimestamp(),
	}); err != nil {
		return err
	}

	scoreType := constants.ScoreTypeIncr
	if score < 0 {
		scoreType = constants.ScoreTypeDecr
	}
	err := UserScoreLogService.Create(&model.UserScoreLog{
		UserId:      userId,
		SourceType:  sourceType,
		SourceId:    sourceId,
		Description: description,
		Type:        scoreType,
		Score:       score,
		CreateTime:  date.NowTimestamp(),
	})
	if err == nil {
		cache.UserCache.Invalidate(userId)
	}
	return err
}
*/

// UpdatePassword 修改密码
func (s *userService) UpdatePassword(userId int64, oldPassword, password, rePassword string) error {
	if err := validate.IsPassword(password, rePassword); err != nil {
		return err
	}
	entity, _ := s.SelectRecordById(userId)

	if len(entity.Password) == 0 {
		return errors.New("你没设置密码，请先设置密码")
	}

	if !tools.ValidatePassword(entity.Password, oldPassword) {
		return errors.New("旧密码验证失败")
	}

	entity.Password = tools.EncodePassword(password)
	_, err := user.UserDao.Update(entity)

	return err
}

// 浏览数+1
func (s *userService) IncrFollowCount(followId int64) {
	user.UserDao.IncrFollowCount(followId)
}

func (s *userService) DecrFollowCount(followId int64) {
	user.UserDao.DecrFollowCount(followId)
}

// 浏览数+1
func (s *userService) IncrFollowedCount(followedId int64) {
	user.UserDao.IncrFollowedCount(followedId)
}

func (s *userService) DecrFollowedCount(followedId int64) {
	user.UserDao.DecrFollowedCount(followedId)
}
